#pragma warning(disable : 4996)

#if USE_FONTS_FROM_WINDOWS
#include <windows.h>
#define ONE_PAST_MAX_FONT_CODEPOINT_COUNT (0x10FFFF + 1)
#define MAX_FONT_WIDHT 1024
#define MAX_FONT_HEIGHT 1024
#include "game_file_format.h"
#include "game_math.h"
#include "test_asset_builder.h"
#include <stdio.h>

global_variable VOID *GlobalFontBits;
global_variable HDC GlobalFontDeviceContext;
#else
#define STB_TRUETYPE_IMPLEMENTATION 1
#include "stb_truetype.h"
#endif
#pragma pack(push, 1)  // 保存当前打包对齐方式，并设置新的打包对齐方式为 1 字节
struct bitmap_header {
    uint16 FileType;  // 文件类型标识符；通常是 'BM'（0x4D42），表示有效的 BMP 文件
    uint32 FileSize;  // BMP 文件的总大小（以字节为单位），包括文件头、信息头和位图数据
    uint16 Reserved1;  // 保留字段；未使用，必须为零
    uint16 Reserved2;  // 保留字段；未使用，必须为零
    uint32 BitmapOffset;  // 文件起始位置到位图数据（像素数据）的偏移量（以字节为单位）
    uint32 Size;  // 信息头的大小；对于 BITMAPINFOHEADER 通常为 40 字节
    int32 Width;  // 图像的宽度（以像素为单位）
    int32 Height;  // 图像的高度（以像素为单位）；正值表示从底到顶，负值表示从顶到底
    uint16 Planes;        // 颜色平面数；必须为 1（历史遗留字段）
    uint16 BitsPerPixel;  // 每像素的位数；常见值有 1（单色）、4（16 色）、8（256 色）、24（真彩色）
    uint32 Compression;   // 压缩类型；例如：
                          // 0 表示不压缩（BI_RGB）
                          // 1 表示 RLE-8 压缩（BI_RLE8）
                          // 2 表示 RLE-4 压缩（BI_RLE4）
                          // 3 表示使用位域（BI_BITFIELDS）
                          // 其他值可能表示自定义或不常见的压缩格式
    uint32 SizeOfBitmap;
    // 图像数据的大小（以字节为单位）；包括所有像素数据的总大小
    // 如果 Compression 为 0（未压缩），可以计算为 (宽度 × 高度 × 每像素字节数)
    int32 HorzResolution;  // 水平分辨率（像素/米）；表示图像的水平打印密度
    int32 VertResolution;  // 垂直分辨率（像素/米）；表示图像的垂直打印密度
    uint32 ColorUser;
    // 使用的颜色数；通常与调色板相关：
    // 0 表示使用默认的颜色数（与位深度匹配的最大颜色数）
    uint32 ColorsImportant;
    // 重要的颜色数；0 表示所有颜色都是重要的
    // 可以用于优化图像加载或显示，仅使用重要颜色

    // 以下字段仅在 Compression 为 3（BI_BITFIELDS）时有效
    uint32 RedMask;    // 用于提取红色分量的位掩码
                       // 指定存储红色通道的位的位置
    uint32 GreenMask;  // 用于提取绿色分量的位掩码
                       // 指定存储绿色通道的位的位置
    uint32 BlueMask;   // 用于提取蓝色分量的位掩码
                       // 指定存储蓝色通道的位的位置
};

struct WAVE_Header {
    uint32 RIFFID;
    uint32 Size;
    uint32 WAVEID;
};

#define RIFF_CODE(a, b, c, d) \
    (((uint32)(a) << 0) | ((uint32)(b) << 8) | ((uint32)(c) << 16) | ((uint32)(d) << 24))
enum {
    WAVE_ChunkID_fmt = RIFF_CODE('f', 'm', 't', ' '),
    WAVE_ChunkID_data = RIFF_CODE('d', 'a', 't', 'a'),
    WAVE_ChunkID_RIFF = RIFF_CODE('R', 'I', 'F', 'F'),
    WAVE_ChunkID_WAVE = RIFF_CODE('W', 'A', 'V', 'E'),
};
struct WAVE_chunk {
    uint32 ID;
    uint32 Size;
};
struct WAVE_fmt {
    uint16 wFormatTag;
    uint16 nChannels;
    uint32 nSamplesPerSec;
    uint32 nAvgBytesPerSec;
    uint16 nBlockAlign;
    uint16 wBitsPerSample;
    uint16 cbSize;
    uint16 wValidBitsPerSample;
    uint32 dwChannelMask;
    uint8 SubFormat[16];
};
#pragma pack(pop)

// 定义一个结构体 `loaded_bitmap`，用于存储加载的位图信息
struct loaded_bitmap {
    int32 Width;   // 图像的宽度（像素数）
    int32 Height;  // 图像的高度（像素数）
    int32 Pitch;
    void *Memory;  // 指向图像像素数据的指针，存储像素的颜色值
    void *Free;
};

struct entire_file {
    uint32 ContentsSize;
    void *Contents;
};

entire_file ReadEntireFile(const char *FileName) {
    entire_file Result = {};
    FILE *In = fopen(FileName, "rb");
    if (In) {
        fseek(In, 0, SEEK_END);
        Result.ContentsSize = ftell(In);
        fseek(In, 0, SEEK_SET);
        Result.Contents = malloc(Result.ContentsSize);
        fread(Result.Contents, Result.ContentsSize, 1, In);
        fclose(In);
    } else {
        printf("ERROR:Cann't open file %s\n", FileName);
    }
    return Result;
}
struct bit_scan_result {
    bool32 Found;
    uint32 Index;
};

// BitScanForward 函数查找给定值中第一个为 1 的位的位置
internal bit_scan_result BitScanForward(uint32 Value) {
    bit_scan_result Result = {};  // 变量，表示是否找到了 1 位，默认值为 false

#if COMPILER_MSVC
    Result.Found = _BitScanForward((unsigned long *)&Result.Index, Value);
#endif

    // 遍历 Value 的每一位（共 32 位）
    for (uint32 Test = 0; Test < 32; ++Test) {
        // 检查当前位是否为 1
        if (Value & (1 << Test)) {
            Result.Index = Test;  // 如果当前位是 1，将位的位置（Test）赋给 Index
            Result.Found = true;  // 标记找到了 1 位
            break;                // 找到后退出循环
        }
    }
    // 如果找到了 1 位，返回 true；否则返回 false
    return Result;
}

internal loaded_bitmap LoadBMP(const char *FileName) {
    loaded_bitmap Result = {};
    // 注意：内存中的字节顺序是 AA BB GG RR，从底部到顶部
    // 在小端->0xRRGGBBAA

    // 调用 ReadEntireFile 函数以读取指定文件的整个内容
    entire_file ReadResult = ReadEntireFile(FileName);
    // 如果文件成功读取
    if (ReadResult.ContentsSize) {
        Result.Free = ReadResult.Contents;
        // 将文件内容强制转换为 bitmap_header 类型，以访问文件头信息
        bitmap_header *Header = (bitmap_header *)ReadResult.Contents;
        // 计算位图数据起始位置
        // 从文件内容中计算图像像素数据的开始位置
        Result.Memory = (uint32 *)((uint8 *)ReadResult.Contents + Header->BitmapOffset);
        Result.Width = Header->Width;
        Result.Height = Header->Height;
        // 定义一个指向像素数据的指针
        Assert(Result.Height >= 0);
#if 1
        uint32 *SourceDest = (uint32 *)Result.Memory;
        // 注意：如果你出于某种原因以通用方式使用这个，请记住 BMP 文件
        // 可以有两种方向，并且对于自上而下的图像，高度会是负数。

        int32 RedMask = Header->RedMask;                      // 获取红色通道掩码
        int32 GreenMask = Header->GreenMask;                  // 获取绿色通道掩码
        int32 BlueMask = Header->BlueMask;                    // 获取蓝色通道掩码
        int32 AlphaMask = ~(RedMask | GreenMask | BlueMask);  // 计算 alpha 通道掩码

        // 初始化每个颜色通道的位移量为 0，后续通过 BitScanForward 找到每个颜色的实际位移
        bit_scan_result RedScan = {};    // 红色通道的位移
        bit_scan_result GreenScan = {};  // 绿色通道的位移
        bit_scan_result BlueScan = {};   // 蓝色通道的位移
        bit_scan_result AlphaScan = {};  // Alpha 通道的位移

        // 调用 BitScanForward 查找红色通道的第一个 1 位，并记录该位的位置
        RedScan = BitScanForward(RedMask);

        // 调用 BitScanForward 查找绿色通道的第一个 1 位，并记录该位的位置
        GreenScan = BitScanForward(GreenMask);

        // 调用 BitScanForward 查找蓝色通道的第一个 1 位，并记录该位的位置
        BlueScan = BitScanForward(BlueMask);

        // 调用 BitScanForward 查找 alpha 通道的第一个 1 位，并记录该位的位置
        AlphaScan = BitScanForward(AlphaMask);

        Assert(RedScan.Found);
        Assert(GreenScan.Found);
        Assert(BlueScan.Found);
        Assert(AlphaScan.Found);
        // RedShiftDown 获取红色分量的位移量，通过 RedScan.Index 计算。
        int32 RedShiftDown = (int32)RedScan.Index;

        // GreenShiftDown 获取绿色分量的位移量，通过 GreenScan.Index 计算。
        int32 GreenShiftDown = (int32)GreenScan.Index;

        // BlueShiftDown 获取蓝色分量的位移量，通过 BlueScan.Index 计算。
        int32 BlueShiftDown = (int32)BlueScan.Index;

        // AlphaShiftDown 获取透明度分量的位移量，通过 AlphaScan.Index 计算。
        int32 AlphaShiftDown = (int32)AlphaScan.Index;

        // 遍历图像的每一行（高度）
        for (int32 Y = 0; Y < Header->Width; ++Y) {
            // 遍历图像的每一列（宽度）
            for (int32 X = 0; X < Header->Height; ++X) {
                // 对每个像素执行字节顺序调整操作
                // 通过位移操作将字节从右移到左，调整颜色通道的顺序。
                // *SourceDest >> 8: 将源像素右移8位，把前面的RGB通道一起往后移动八位
                // *SourceDest << 24: 将源像素低八位左移24位，到最前面
                uint32 C = *SourceDest;
#if 0
*SourceDest = ((((C >> AlphaShift.Index) & 0xFF) << 24) |  //
(((C >> RedShift.Index) & 0xFF) << 16) |    //
(((C >> GreenShift.Index) & 0xFF) << 8) |   //
(((C >> BlueShift.Index) & 0xFF) << 0));
#else
                v4 Texel = {
                    // 通过按位与操作和红色掩码提取红色分量，并右移至最低有效位。
                    // R = (C & RedMask) 提取红色分量，>> RedShiftDown 将其右移至合适的位置。
                    real32((C & RedMask) >> RedShiftDown),

                    // 通过按位与操作和绿色掩码提取绿色分量，并右移至最低有效位。
                    // G = (C & GreenMask) 提取绿色分量，>> GreenShiftDown 将其右移至合适的位置。
                    real32((C & GreenMask) >> GreenShiftDown),

                    // 通过按位与操作和蓝色掩码提取蓝色分量，并右移至最低有效位。
                    // B = (C & BlueMask) 提取蓝色分量，>> BlueShiftDown 将其右移至合适的位置。
                    real32((C & BlueMask) >> BlueShiftDown),

                    // 通过按位与操作和透明度掩码提取透明度分量，并右移至最低有效位。
                    // A = (C & AlphaMask) 提取透明度分量，>> AlphaShiftDown 将其右移至合适的位置。
                    real32((C & AlphaMask) >> AlphaShiftDown)};
                Texel = SRGB255ToLinear1(Texel);
#if 1
                Texel.rgb *= Texel.a;
#endif
                Texel = Linear1ToSRGB255(Texel);

                // 将各个颜色分量分别左移至相应的位置，并通过按位或操作组合成最终的颜色值。
                // 使用左移操作将每个分量移到正确的位位置，然后按位或操作合并所有分量。
                *SourceDest = (((uint32)(Texel.a + 0.5f) << 24) |  // 将红色分量移至高16位
                               ((uint32)(Texel.r + 0.5f) << 16) |  // 将绿色分量移至中间8位
                               ((uint32)(Texel.g + 0.5f) << 8) |   // 将蓝色分量移至低8位
                               ((uint32)(Texel.b + 0.5f) << 0));  // 将透明度分量移至高24位

#endif
#if 0
// 提取当前像素的颜色分量
uint32 C0 = *SourceDest & 0x000000FF;  // 提取Alpha通道的值
uint32 C1 = *SourceDest & 0x0000FF00;  // 提取Blue通道的值
uint32 C2 = *SourceDest & 0x00FF0000;  // 提取Green通道的值
uint32 C3 = *SourceDest & 0xFF000000;  // 提取Red通道的值

// 重新排列颜色通道顺序
// Alpha通道（C0）移动到最高位（左移24位）
// Red通道（C3）移动到次高位（左移16位）
// Green通道（C2）移动到次低位（左移8位）
// Blue通道（C1）移动到最低位（右移0位）
*SourceDest = (C0 << 24) | (C3 >> 8) | (C2 >> 8) | (C1 >> 8);
#endif
                // 移动指针到下一个像素
                ++SourceDest;
            }
        }

#endif
    }
    Result.Pitch = Result.Width * BITMAP_BYTES_PER_PIEXLE;
#if 0
Result.Memory = (uint8 *)Result.Memory + Result.Pitch * (Result.Height - 1);
Result.Pitch = - Result.Pitch;
#endif
    // 如果文件没有成功读取，则返回 NULL
    return Result;
}

struct riff_iterator {
    uint8 *At;
    uint8 *Stop;
};

inline riff_iterator ParseChunkAt(void *At, void *Stop) {
    riff_iterator Iter;
    Iter.At = (uint8 *)At;
    Iter.Stop = (uint8 *)Stop;
    return Iter;
}

inline riff_iterator NextChunk(riff_iterator Iter) {
    WAVE_chunk *Chunk = (WAVE_chunk *)Iter.At;
    uint32 Size = (Chunk->Size + 1) & ~1;
    Iter.At += sizeof(WAVE_chunk) + Size;
    return Iter;
}

inline bool32 IsValid(riff_iterator Iter) {
    bool32 Result = Iter.At < Iter.Stop;
    return Result;
}

inline void *GetChunkData(riff_iterator Iter) {
    void *Result = Iter.At + sizeof(WAVE_chunk);
    return Result;
}

inline uint32 GetType(riff_iterator Iter) {
    WAVE_chunk *Chunk = (WAVE_chunk *)Iter.At;
    uint32 Result = Chunk->ID;
    return Result;
}

inline uint32 GetChunkDataSize(riff_iterator Iter) {
    WAVE_chunk *Chunk = (WAVE_chunk *)Iter.At;
    uint32 Result = Chunk->Size;
    return Result;
}

struct loaded_sound {
    uint32 SampleCount;
    uint32 ChannelCount;
    int16 *Samples[2];
    void *Free;
};

internal loaded_sound LoadWAV(const char *FileName, uint32 SectionFirstSampleIndex,
                              uint32 SectionSampleCount) {
    loaded_sound Result = {};
    uint32 SampleCount = {};
    entire_file ReadResult = ReadEntireFile(FileName);
    if (ReadResult.ContentsSize != 0) {
        Result.Free = ReadResult.Contents;
        WAVE_Header *Header = (WAVE_Header *)ReadResult.Contents;
        Assert(Header->RIFFID == WAVE_ChunkID_RIFF);
        Assert(Header->WAVEID == WAVE_ChunkID_WAVE);

        int16 *SampleData = 0;
        uint32 ChannelCount = 0;
        uint32 SampleDataSize = 0;
        for (riff_iterator Iter =
                 ParseChunkAt(Header + 1, (uint8 *)(Header + 1) + Header->Size - 4);
             IsValid(Iter); Iter = NextChunk(Iter)) {
            switch (GetType(Iter)) {
                case WAVE_ChunkID_fmt: {
                    WAVE_fmt *fmt = (WAVE_fmt *)GetChunkData(Iter);
                    Assert(fmt->wFormatTag == 1);  // 只支持PCM
                    Assert(fmt->nSamplesPerSec = 48000);
                    Assert(fmt->wBitsPerSample == 16);
                    Assert(fmt->nBlockAlign == (sizeof(uint16) * fmt->nChannels));
                    ChannelCount = fmt->nChannels;
                } break;
                case WAVE_ChunkID_data: {
                    SampleData = (int16 *)GetChunkData(Iter);
                    SampleDataSize = GetChunkDataSize(Iter);
                } break;
            }
        }
        Assert(ChannelCount && SampleData);
        Result.ChannelCount = ChannelCount;
        SampleCount = SampleDataSize / (ChannelCount * sizeof(int16));
        if (ChannelCount == 1) {
            Result.Samples[0] = SampleData;
            Result.Samples[1] = 0;
        } else if (ChannelCount == 2) {
            Result.Samples[0] = SampleData;
            Result.Samples[1] = SampleData + SampleCount;
#if 0
            for (uint32 SampleIndex = 0; SampleIndex < Result.SampleCount; ++SampleIndex) {
                SampleData[2 * SampleIndex + 0] = (uint16)SampleIndex;
                SampleData[2 * SampleIndex + 1] = (uint16)SampleIndex;
            }

#endif
            for (uint32 SampleIndex = 0; SampleIndex < SampleCount; ++SampleIndex) {
                uint16 Source = SampleData[2 * SampleIndex];
                SampleData[2 * SampleIndex] = SampleData[SampleIndex];
                SampleData[SampleIndex] = Source;
            }
        } else {
            Assert(!"Invalid channel count in WAV file!");
        }
    }
    Result.ChannelCount = 1;
    bool32 AtEnd = true;
    if (SectionSampleCount) {
        Assert((SectionFirstSampleIndex + SectionSampleCount) <= SampleCount);
        AtEnd = ((SectionFirstSampleIndex + SectionSampleCount) == SampleCount);
        SampleCount = SectionSampleCount;
        for (uint32 ChannelIndex = 0; ChannelIndex < Result.ChannelCount; ChannelIndex++) {
            Result.Samples[ChannelIndex] += SectionFirstSampleIndex;
        }
    }

    if (AtEnd) {
        for (uint32 ChannelIndex = 0; ChannelIndex < Result.ChannelCount; ChannelIndex++) {
            for (uint32 SampleIndex = SampleCount; SampleIndex < (SampleCount + 8); ++SampleIndex) {
                Result.Samples[ChannelIndex][SampleIndex] = 0;
            }
        }
    }
    Result.SampleCount = SampleCount;
    return Result;
}

internal loaded_font *LoadFont(const char *FileName, const char *FontName, int PixelHeight) {
    loaded_font *Font = (loaded_font *)malloc(sizeof(loaded_font));
    AddFontResourceExA(FileName, FR_PRIVATE, 0);

    Font->Win32Handle = CreateFontA(PixelHeight, 0, 0, 0,                      //
                                    FW_NORMAL, FALSE, FALSE, FALSE,            //
                                    DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,       //
                                    CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,  //
                                    DEFAULT_PITCH | FF_DONTCARE,               //
                                    FontName);
    SelectObject(GlobalFontDeviceContext, Font->Win32Handle);
    GetTextMetrics(GlobalFontDeviceContext, &Font->TextMetric);
    Font->MinCodePoint = INT_MAX;
    Font->MaxCodePoint = 0;
    Font->MaxGlyphCount = 5000;
    Font->GlyphCount = 0;

    uint32 GlyphIndexFromCodePointSize = ONE_PAST_MAX_FONT_CODEPOINT_COUNT * sizeof(uint32);
    Font->GlyphIndexFromCodePoint = (uint32 *)malloc(GlyphIndexFromCodePointSize);
    memset(Font->GlyphIndexFromCodePoint, 0, GlyphIndexFromCodePointSize);

    Font->Glyphs = (hha_font_glyph *)malloc(sizeof(hha_font_glyph) * Font->MaxGlyphCount);
    size_t HorizontalAdvanceSize = sizeof(real32) * Font->MaxGlyphCount * Font->MaxGlyphCount;
    Font->HorizontalAdvance = (real32 *)malloc(HorizontalAdvanceSize);
    memset(Font->HorizontalAdvance, 0, HorizontalAdvanceSize);

    Font->GlyphCount = 1;
    Font->OnePastHighestCodePoint = 0;
    Font->Glyphs[0].UnicodeCodePoint = 0;
    Font->Glyphs[0].BitmapID.Value = 0;
    return Font;
}

internal void FinalizeFontKerning(loaded_font *Font) {
    SelectObject(GlobalFontDeviceContext, Font->Win32Handle);
    DWORD KerningPairCount = GetKerningPairsW(GlobalFontDeviceContext, 0, 0);
    KERNINGPAIR *KerningPairs = (KERNINGPAIR *)malloc(KerningPairCount * sizeof(KERNINGPAIR));
    GetKerningPairsW(GlobalFontDeviceContext, KerningPairCount, KerningPairs);
    for (DWORD KerningPairIndex = 0; KerningPairIndex < KerningPairCount; ++KerningPairIndex) {
        KERNINGPAIR *Pair = KerningPairs + KerningPairIndex;
        if (((uint32)Pair->wFirst < ONE_PAST_MAX_FONT_CODEPOINT_COUNT) &&  //
            ((uint32)Pair->wSecond < ONE_PAST_MAX_FONT_CODEPOINT_COUNT)) {
            uint32 First = Font->GlyphIndexFromCodePoint[Pair->wFirst];
            uint32 Second = Font->GlyphIndexFromCodePoint[Pair->wSecond];
            if ((First != 0) && (Second != 0)) {
                Font->HorizontalAdvance[First * Font->MaxGlyphCount + Second] +=
                    (real32)Pair->iKernAmount;
            }
        }
    }
    free(KerningPairs);
}

internal void FreeFont(loaded_font *Font) {  //
    if (Font) {
        DeleteObject(Font->Win32Handle);
        free(Font->GlyphIndexFromCodePoint);
        free(Font->Glyphs);
        free(Font->HorizontalAdvance);
        free(Font);
    }
}

internal void InitializeFontDC(void) {
    GlobalFontDeviceContext = CreateCompatibleDC(GetDC(0));
    BITMAPINFO Info = {};
    Info.bmiHeader.biSize = sizeof(Info.bmiHeader);
    Info.bmiHeader.biWidth = MAX_FONT_WIDHT;
    Info.bmiHeader.biHeight = MAX_FONT_HEIGHT;
    Info.bmiHeader.biPlanes = 1;
    Info.bmiHeader.biBitCount = 32;
    Info.bmiHeader.biCompression = BI_RGB;
    Info.bmiHeader.biSizeImage = 0;
    Info.bmiHeader.biXPelsPerMeter = 0;
    Info.bmiHeader.biYPelsPerMeter = 0;
    Info.bmiHeader.biClrUsed = 0;
    Info.bmiHeader.biClrImportant = 0;
    HBITMAP Bitmap =
        CreateDIBSection(GlobalFontDeviceContext, &Info, DIB_RGB_COLORS, &GlobalFontBits, 0, 0);
    SelectObject(GlobalFontDeviceContext, Bitmap);
    SetBkColor(GlobalFontDeviceContext, RGB(0, 0, 0));
}
internal loaded_bitmap LoadGlyphBitmap(loaded_font *Font,  //
                                       uint32_t CodePoint, hha_asset *Asset) {
    loaded_bitmap Result = {};
    uint32 GlyphIndex = Font->GlyphIndexFromCodePoint[CodePoint];
#if USE_FONTS_FROM_WINDOWS
    SelectObject(GlobalFontDeviceContext, Font->Win32Handle);
    memset(GlobalFontBits, 0x00, MAX_FONT_WIDHT * MAX_FONT_HEIGHT * sizeof(uint32));

    wchar_t CheesePoint = (wchar_t)CodePoint;
    SIZE Size;
    GetTextExtentPoint32W(GlobalFontDeviceContext, &CheesePoint, 1, &Size);

    int PreStepX = 128;
    int BoundWidth = Size.cx + PreStepX;
    if (BoundWidth > MAX_FONT_WIDHT) {
        BoundWidth = MAX_FONT_WIDHT;
    }
    int BoundHeight = Size.cy;
    if (BoundHeight > MAX_FONT_HEIGHT) {
        BoundHeight = MAX_FONT_HEIGHT;
    }

    // PatBlt(DeviceContext, 0, 0, Width, Height, BLACKNESS);
    SetTextColor(GlobalFontDeviceContext, RGB(255, 255, 255));
    // SetTextColor(DeviceContext, RGB(0, 0, 0));

    TextOutW(GlobalFontDeviceContext, PreStepX, 0, &CheesePoint, 1);

    int32 MinX = 10000;
    int32 MinY = 10000;
    int32 MaxX = -10000;
    int32 MaxY = -10000;
    uint32 *Row = (uint32 *)GlobalFontBits + (MAX_FONT_HEIGHT - 1) * MAX_FONT_WIDHT;
    for (int32 Y = 0; Y < BoundHeight; Y++) {
        uint32 *Pixel = Row;
        for (int32 X = 0; X < BoundWidth; X++) {
            if (*Pixel != 0) {
                if (MinX > X) {
                    MinX = X;
                }
                if (MinY > Y) {
                    MinY = Y;
                }
                if (MaxX < X) {
                    MaxX = X;
                }
                if (MaxY < Y) {
                    MaxY = Y;
                }
            }
            ++Pixel;
        }
        Row -= MAX_FONT_WIDHT;
    }
    real32 KerningChange = 0;
    if (MinX <= MaxX) {
#if 0
        // TODO(casey): Apron!
        --MinX;
        --MinY;
        ++MaxX;
        ++MaxY;
#endif
        int Width = (MaxX - MinX) + 1;
        int Height = (MaxY - MinY) + 1;
        Result.Width = Width + 2;
        Result.Height = Height + 2;
        Result.Pitch = Result.Width * BITMAP_BYTES_PER_PIEXLE;
        Result.Memory = malloc(Result.Height * Result.Pitch);
        Result.Free = Result.Memory;
        memset(Result.Memory, 0, Result.Height * Result.Pitch);
        uint8 *DestRow = (uint8 *)Result.Memory + (Result.Height - 1 - 1) * Result.Pitch;
        uint32 *SourceRow =
            (uint32 *)GlobalFontBits + (MAX_FONT_HEIGHT - 1 - MinY) * MAX_FONT_WIDHT;
        for (int Y = MinY; Y <= MaxY; ++Y) {
            uint32 *Source = (uint32 *)SourceRow + MinX;
            uint32 *Dest = (uint32 *)DestRow + 1;
            for (int32 X = MinX; X <= MaxX; ++X) {
#if 0
                COLORREF Pixel = GetPixel(DeviceContext, X, Y);
#else
                uint32 Pixel = *Source;
#endif
                real32 Gray = (real32)(Pixel & 0xFF);
                v4 Texel = {255.0f, 255.0f, 255.0f, Gray};
                Texel = SRGB255ToLinear1(Texel);
                Texel.rgb *= Texel.a;
                Texel = Linear1ToSRGB255(Texel);
                *Dest++ =
                    (((uint32)(Texel.a + 0.5f) << 24)  //
                     | ((uint32)(Texel.r + 0.5f) << 16)  // 将红色通道值放到目标像素的红色位置
                     | ((uint32)(Texel.g + 0.5f) << 8)  // 将绿色通道值放到目标像素的绿色位置
                     | ((uint32)(Texel.b + 0.5f) << 0));  // 将蓝色通道值放到目标像素的蓝色位置

                ++Source;
            }
            DestRow -= Result.Pitch;
            SourceRow -= MAX_FONT_WIDHT;
        }
        Asset->Bitmap.AlignPercentage[0] = (1.0f) / (real32)Result.Width;
        Asset->Bitmap.AlignPercentage[1] =
            (1.0f + (MaxY - (BoundHeight - Font->TextMetric.tmDescent))) / (real32)Result.Height;
        KerningChange = (real32)(MinX - PreStepX);
    }

#if 0
        ABC ThisABC;
        GetCharABCWidthsW(GlobalFontDeviceContext, CodePoint, CodePoint, &ThisABC);
        real32 CharAdvance = (real32)ThisABC.abcA + (real32)ThisABC.abcB + (real32)ThisABC.abcC;
#else
    INT ThisWidth;
    GetCharWidth32W(GlobalFontDeviceContext, CodePoint, CodePoint, &ThisWidth);
    real32 CharAdvance = (real32)ThisWidth;
#endif
    for (uint32 OtherGlyphIndex = 0; OtherGlyphIndex < Font->MaxGlyphCount; ++OtherGlyphIndex) {
        Font->HorizontalAdvance[GlyphIndex * Font->MaxGlyphCount + OtherGlyphIndex] +=
            CharAdvance - KerningChange;
        if (OtherGlyphIndex != 0) {
            Font->HorizontalAdvance[OtherGlyphIndex * Font->MaxGlyphCount + GlyphIndex] +=
                KerningChange;
        }
    }
#else
    (void)FontName;
    entire_file TTFFile = ReadEntireFile(FileName);
    if (TTFFile.Contents != 0) {
        stbtt_fontinfo Font;
        stbtt_InitFont(&Font, (const unsigned char *)TTFFile.Contents,  //
                       stbtt_GetFontOffsetForIndex((const unsigned char *)TTFFile.Contents, 0));
        int Width, Height, XOffset, YOffset;
        uint8 *MonoBitmap =
            stbtt_GetCodepointBitmap(&Font, 0,                                  //
                                     stbtt_ScaleForPixelHeight(&Font, 128.0f),  //
                                     CodePoint, &Width, &Height, &XOffset, &YOffset);

        Result.Width = Width;
        Result.Height = Height;
        Result.Pitch = Result.Width * BITMAP_BYTES_PER_PIEXLE;
        Result.Memory = malloc(Height * Result.Pitch);
        Result.Free = Result.Memory;

        uint8 *Source = MonoBitmap;
        uint8 *DestRow = (uint8 *)Result.Memory + (Height - 1) * Result.Pitch;
        for (int Y = 0; Y < Height; Y++) {
            uint32 *Dest = (uint32 *)DestRow;
            for (int X = 0; X < Width; X++) {
                uint8 Gray = *Source++;
                uint8 Alpha = 0xFF;

                *Dest++ = ((Alpha << 24) |  //
                           (Gray << 16) |   //
                           (Gray << 8) |    //
                           (Gray << 0));
            }
            DestRow -= Result.Pitch;
        }
        stbtt_FreeBitmap(MonoBitmap, 0);
        free(TTFFile.Contents);
    }
#endif
    return Result;
}

internal void WriteHHA(game_assets *Assets, const char *FileName) {
    FILE *Out = fopen(FileName, "wb");
    if (Out) {
        hha_header Header = {};
        Header.MagicValue = HHA_MAGIC_VALUE;
        Header.Version = HHA_VERSION;
        Header.TagCount = Assets->TagCount;
        Header.AssetTypeCount = Asset_Count;
        Header.AssetCount = Assets->AssetCount;

        uint32 TagArraySize = Header.TagCount * sizeof(hha_tag);
        uint32 AssetTypeArraySize = Header.AssetTypeCount * sizeof(hha_asset_type);
        uint32 AssetArraySize = Header.AssetCount * sizeof(hha_asset);

        Header.Tags = sizeof(Header);
        Header.AssetTypes = Header.Tags + TagArraySize;
        Header.Assets = Header.AssetTypes + AssetTypeArraySize;

        fwrite(&Header, sizeof(Header), 1, Out);
        fwrite(&Assets->Tags, TagArraySize, 1, Out);
        fwrite(&Assets->AssetTypes, AssetTypeArraySize, 1, Out);
        fseek(Out, AssetArraySize, SEEK_CUR);
        for (uint32 AssetIndex = 1; AssetIndex < Header.AssetCount; ++AssetIndex) {
            asset_source *Source = Assets->AssetSource + AssetIndex;
            hha_asset *Dest = Assets->Assets + AssetIndex;
            Dest->DataOffset = ftell(Out);
            if (Source->Type == AssetType_Sound) {
                loaded_sound WAV = LoadWAV(Source->Sound.FileName,          //
                                           Source->Sound.FirstSampleIndex,  //
                                           Dest->Sound.SampleCount);
                Dest->Sound.SampleCount = WAV.SampleCount;
                Dest->Sound.ChennelCount = WAV.ChannelCount;
                for (uint32 ChannelIndex = 0; ChannelIndex < WAV.ChannelCount; ++ChannelIndex) {
                    fwrite(WAV.Samples[ChannelIndex], Dest->Sound.SampleCount * sizeof(int16), 1,
                           Out);
                }
                free(WAV.Free);
            } else if (Source->Type == AssetType_Font) {
                loaded_font *Font = Source->Font.Font;
                FinalizeFontKerning(Font);
                uint32 GlyphSize = Font->GlyphCount * sizeof(hha_font_glyph);
                fwrite(Font->Glyphs, GlyphSize, 1, Out);
                uint8 *HorizontalAdvance = (uint8 *)Font->HorizontalAdvance;
                for (uint32 GlyphIndex = 0; GlyphIndex < Font->GlyphCount; ++GlyphIndex) {
                    uint32 HorizontalAdvanceSize = sizeof(real32) * Font->GlyphCount;
                    fwrite(HorizontalAdvance, HorizontalAdvanceSize, 1, Out);
                    HorizontalAdvance += sizeof(real32) * Font->MaxGlyphCount;
                }
            } else {
                loaded_bitmap Bitmap = {};
                if (Source->Type == AssetType_FontGlyph) {
                    Bitmap = LoadGlyphBitmap(Source->Glyph.Font, Source->Glyph.CodePoint, Dest);
                } else {
                    Assert(Source->Type == AssetType_Bitmap);
                    Bitmap = LoadBMP(Source->Bitmap.FileName);
                }

                Dest->Bitmap.Dim[0] = Bitmap.Width;
                Dest->Bitmap.Dim[1] = Bitmap.Height;
                Assert(Bitmap.Width * 4 == Bitmap.Pitch);
                fwrite(Bitmap.Memory, Bitmap.Width * Bitmap.Height * 4, 1, Out);
                free(Bitmap.Free);
            }
        }
        fseek(Out, (uint32)Header.Assets, SEEK_SET);
        fwrite(Assets->Assets, AssetArraySize, 1, Out);
    } else {
        printf("Error,Couldn't open file file :(\n");
    }
    fclose(Out);
}

internal void Initialize(game_assets *Assets) {
    Assets->TagCount = 1;
    Assets->AssetCount = 1;
    Assets->AssetIndex = 0;
    Assets->AssetTypeCount = 1;
    Assets->DEBUGAssetType = 0;
}

internal void WriteHero(void) {
    game_assets Assets_{};
    game_assets *Assets = &Assets_;
    Initialize(Assets);

    real32 AngleRight = 0.0f * Tau32;
    real32 AngleBack = 0.25f * Tau32;
    real32 AngleLeft = 0.5f * Tau32;
    real32 AngleFront = 0.75f * Tau32;

    BeginAssetType(Assets, Asset_Head);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_right_head.bmp", 0.5f,
                   0.156682029f);
    AddTag(Assets, Tag_FacingDirection, AngleRight);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_back_head.bmp", 0.5f,
                   0.156682029f);
    AddTag(Assets, Tag_FacingDirection, AngleBack);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_left_head.bmp", 0.5f,
                   0.156682029f);
    AddTag(Assets, Tag_FacingDirection, AngleLeft);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_front_head.bmp", 0.5f,
                   0.156682029f);
    AddTag(Assets, Tag_FacingDirection, AngleFront);
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Cape);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_right_cape.bmp", 0.5f,
                   0.156682029f);
    AddTag(Assets, Tag_FacingDirection, AngleRight);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_back_cape.bmp", 0.5f,
                   0.156682029f);
    AddTag(Assets, Tag_FacingDirection, AngleBack);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_left_cape.bmp", 0.5f,
                   0.156682029f);
    AddTag(Assets, Tag_FacingDirection, AngleLeft);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_front_cape.bmp", 0.5f,
                   0.156682029f);
    AddTag(Assets, Tag_FacingDirection, AngleFront);
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Torso);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_right_torso.bmp", 0.5f,
                   0.156682029f);
    AddTag(Assets, Tag_FacingDirection, AngleRight);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_back_torso.bmp", 0.5f,
                   0.156682029f);
    AddTag(Assets, Tag_FacingDirection, AngleBack);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_left_torso.bmp", 0.5f,
                   0.156682029f);
    AddTag(Assets, Tag_FacingDirection, AngleLeft);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_front_torso.bmp", 0.5f,
                   0.156682029f);
    AddTag(Assets, Tag_FacingDirection, AngleFront);
    EndAssetType(Assets);

    WriteHHA(Assets, "test1.hha");
}

internal void AddFont(game_assets *Assets, const char *FontFile, const char *FontName,
                      asset_font_type Type) {
    loaded_font *DebugFont = LoadFont(FontFile, FontName, 128);
    // AddCharacterAsset(Assets, "C:/Windows/Fonts/cour.ttf", "Courier New", Character);

    BeginAssetType(Assets, Asset_FontGlyph);
    AddCharacterAsset(Assets, DebugFont, ' ');
    for (uint32 Character = '!'; Character <= '~'; ++Character) {
        AddCharacterAsset(Assets, DebugFont, Character);
    }
    // Kanji OWL
    AddCharacterAsset(Assets, DebugFont, 0x5C0F);
    AddCharacterAsset(Assets, DebugFont, 0x8033);
    AddCharacterAsset(Assets, DebugFont, 0x6728);
    AddCharacterAsset(Assets, DebugFont, 0x514E);

    EndAssetType(Assets);
    BeginAssetType(Assets, Asset_Font);
    AddFontAsset(Assets, DebugFont);
    AddTag(Assets, Tag_FontType, (real32)Type);
    EndAssetType(Assets);
}

internal void WriteFonts(void) {
    game_assets Assets_{};
    game_assets *Assets = &Assets_;
    Initialize(Assets);
    loaded_font *Fonts[] = {
        LoadFont("C:/Windows/Fonts/arial.ttf", "Arial", 128),  //
        LoadFont("C:/Windows/Fonts/simsun.ttc", "simsun", 50)  //
        // LoadFont("C:/Windows/Fonts/calibri.ttf", "calibri", 50)  //
        // LoadFont("C:/Windows/Fonts/arial.ttf", "Arial", 50),  //
    };

    // AddCharacterAsset(Assets, "C:/Windows/Fonts/cour.ttf", "Courier New", Character);

    BeginAssetType(Assets, Asset_FontGlyph);
    for (uint32 FontIndex = 0; FontIndex < ArrayCount(Fonts); ++FontIndex) {
        loaded_font *Font = Fonts[FontIndex];
        AddCharacterAsset(Assets, Font, ' ');
        for (uint32 Character = '!'; Character <= '~'; ++Character) {
            AddCharacterAsset(Assets, Font, Character);
        }
        // Kanji OWL
        AddCharacterAsset(Assets, Font, 0x5C0F);
        AddCharacterAsset(Assets, Font, 0x8033);
        AddCharacterAsset(Assets, Font, 0x6728);
        AddCharacterAsset(Assets, Font, 0x514E);
    }
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Font);
    AddFontAsset(Assets, Fonts[0]);
    AddTag(Assets, Tag_FontType, (real32)FontType_Default);
    AddFontAsset(Assets, Fonts[1]);
    AddTag(Assets, Tag_FontType, (real32)FontType_Debug);
    EndAssetType(Assets);

    WriteHHA(Assets, "testfonts.HHA");
}

internal void WriteNonHero(void) {
    game_assets Assets_{};
    game_assets *Assets = &Assets_;
    Initialize(Assets);

    BeginAssetType(Assets, Asset_Shadow);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_shadow.bmp",
                   0.500000000f, 0.156682029f);
    EndAssetType(Assets);
    BeginAssetType(Assets, Asset_Tree);

    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/tree00.bmp", 0.493827164f,
                   0.295652181f);
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Rock);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/rock03.bmp", 0.344827592f,
                   0.656250000f);
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Grass);
    // 调用 `DebugLoadBMP` 函数加载位图数据
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/grass00.bmp");
    // 调用 `DebugLoadBMP` 函数加载位图数据
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/grass01.bmp");
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Tuft);
    // 调用 `DebugLoadBMP` 函数加载位图数据
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/tuft00.bmp");
    // 调用 `DebugLoadBMP` 函数加载位图数据
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/tuft01.bmp");
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/tuft02.bmp");
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Stone);
    // 调用 `DebugLoadBMP` 函数加载位图数据
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/ground00.bmp");
    // 调用 `DebugLoadBMP` 函数加载位图数据
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/ground01.bmp");
    // 调用 `DebugLoadBMP` 函数加载位图数据
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/ground02.bmp");
    // 调用 `DebugLoadBMP` 函数加载位图数据
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/ground03.bmp");
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Sword);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/rock03.bmp", 0.5f, 0.65625f);
    EndAssetType(Assets);

    WriteHHA(Assets, "test2.hha");
}

internal void WriteSounds() {
    game_assets Assets_{};
    game_assets *Assets = &Assets_;
    Initialize(Assets);

    BeginAssetType(Assets, Asset_Bloop);
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/bloop_00.wav");
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/bloop_01.wav");
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/bloop_02.wav");
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/bloop_03.wav");
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/bloop_04.wav");
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Crack);
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/crack_00.wav");
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Drop);
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/Drop_00.wav");
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Glide);
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/Glide_00.wav");
    EndAssetType(Assets);

    uint32 OneMusicChunk = 10 * 48000;
    uint32 TotalMusicSampleCount = 7468095;
    BeginAssetType(Assets, Asset_Music);
    for (uint32 FirstSampleIndex = 0; FirstSampleIndex < TotalMusicSampleCount;
         FirstSampleIndex += OneMusicChunk) {
        uint32 SampleCount = TotalMusicSampleCount - FirstSampleIndex;
        if (SampleCount > OneMusicChunk) {
            SampleCount = OneMusicChunk;
        }
        sound_id ThisMusic =
            AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/Music_test.wav",
                          FirstSampleIndex, SampleCount);
        if ((FirstSampleIndex + OneMusicChunk) < TotalMusicSampleCount) {
            Assets->Assets[ThisMusic.Value].Sound.Chain = HHASoundChain_Advance;
        }
    }
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Puhp);
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/Puhp_00.wav");
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/Puhp_01.wav");
    EndAssetType(Assets);

    WriteHHA(Assets, "test3.hha");
}

#define _CRT_SECURE_NO_WARNINGS
int main(int ArgCount, char *argv[]) {
    (void)ArgCount;
    (void)argv;
    InitializeFontDC();
    WriteHero();
    WriteNonHero();
    WriteSounds();
    WriteFonts();
}